package com.googlecode.lohr.parser.combinators.grammars;

import static com.googlecode.lohr.parser.combinators.Parsers.choice;
import static com.googlecode.lohr.parser.combinators.Parsers.longest;
import static com.googlecode.lohr.parser.combinators.Parsers.max;
import static com.googlecode.lohr.parser.combinators.Parsers.opt;
import static com.googlecode.lohr.parser.combinators.Parsers.range;
import static com.googlecode.lohr.parser.combinators.Parsers.rep;
import static com.googlecode.lohr.parser.combinators.Parsers.seq;
import static com.googlecode.lohr.parser.combinators.Parsers.string;

import java.util.Arrays;

import com.googlecode.lohr.parser.combinators.MatchResults;
import com.googlecode.lohr.parser.combinators.matcher.MatchContext;
import com.googlecode.lohr.parser.combinators.matcher.Matcher;
import com.googlecode.lohr.parser.combinators.matcher.common.BooleanMatcher;
import com.googlecode.lohr.parser.combinators.matcher.common.DecimalMatcher;
import com.googlecode.lohr.parser.combinators.matcher.common.DoubleMatcher;
import com.googlecode.lohr.parser.combinators.matcher.common.IntegerMatcher;
import com.googlecode.lohr.parser.combinators.matcher.common.NameMatcher;
import com.googlecode.lohr.parser.combinators.matcher.common.QuotedStringMatcher;
import com.googlecode.lohr.parser.combinators.matcher.common.WhitespaceMatcher;
import com.googlecode.lohr.parser.combinators.node.AbstractNode;
import com.googlecode.lohr.parser.combinators.node.StringNode;

/**
 * A grammar for the Terse RDF Triple Language (Turtle) language.
 * @see http://www.w3.org/TeamSubmission/turtle/
 * 
 * @author Ted Stockwell
 */
public class TurtleGrammar
{
	/**
	 * Elements of this grammar
	 */
	public static final Matcher lower = range('a', 'z');
	public static final Matcher digit = range('0', '9');
	public static final Matcher ws = longest(new WhitespaceMatcher());
	public static final Matcher optws = opt(ws);
	public static final Matcher name= longest(new NameMatcher());
	
	public static final Matcher QUOTED_STRING= new QuotedStringMatcher();
	public static final Matcher QNAME= seq(opt(name), string(":"), opt(name));
	public static final Matcher NODE_ID= seq(string("_:"), name);
	public static final Matcher PREFIX_NAME= choice(name);
	public static final Matcher URIREF= longest(new URIRefMatcher());
	public static final Matcher RESOURCE=longest(choice(URIREF, QNAME));
	public static final Matcher SUBJECT=longest(choice(RESOURCE, NODE_ID, string("[]")));
	public static final Matcher BASE=seq(string("@base"), ws, URIREF);
	public static final Matcher PREFIX_ID=seq(string("@prefix"), ws, opt(PREFIX_NAME), string(":"), ws, URIREF);
	public static final Matcher BOOL=new BooleanMatcher();
	public static final Matcher DECIMAL=new DecimalMatcher();
	public static final Matcher DOUBLE=new DoubleMatcher();
	public static final Matcher INTEGER=new IntegerMatcher();
	public static final Matcher DATATYPE_STRING=seq(QUOTED_STRING, string("^^"), RESOURCE);
	public static final Matcher LANGUAGE=seq(rep(1, max, lower), rep(0, max, seq(string("-"), rep(1, max, choice(lower, digit)))));
	public static final Matcher STRING=seq(QUOTED_STRING, opt(seq(string("@"), LANGUAGE)));
	public static final Matcher LITERAL=longest(choice(STRING, DATATYPE_STRING, INTEGER, DOUBLE, DECIMAL, BOOL));
	public static final Matcher OBJECT=longest(choice(RESOURCE, LITERAL, NODE_ID, string("[]")));
	public static final Matcher OBJECT_LIST=seq(OBJECT, longest(rep(0, max, seq(optws, string(","), optws, OBJECT))));
	public static final Matcher VERB=longest(choice(RESOURCE, string("a")));
	public static final Matcher PREDICATE_VALUES=seq(VERB, ws, OBJECT_LIST);
	public static final Matcher TRIPLES=seq(SUBJECT, ws, PREDICATE_VALUES, longest(rep(0, max, seq(optws, string(";"), optws, PREDICATE_VALUES))));
	public static final Matcher DIRECTIVE=choice(PREFIX_ID, BASE);
	public static final Matcher STATEMENT=seq(choice(DIRECTIVE, TRIPLES), optws, string("."));
	public static final Matcher DOCUMENT=seq(optws, longest(rep(1, max, seq(STATEMENT, optws))));
	
	
	
	public static class URIRefMatcher extends Matcher 
	{
		@Override 
		public MatchResults match(MatchContext ctx, int start)
		{
			final String input= ctx.getInput();
			if (input.length() <= start || input.charAt(start) != '<')
				return new MatchResults("Expected '<'", start);
			
			int pos = input.indexOf('>', start);
			if (pos < 0)
				return new MatchResults("Expected end of URI reference", start);
			
			pos++;
			StringNode node= new StringNode(this, start, pos, input.substring(start, pos));
			return new MatchResults(Arrays.<AbstractNode>asList(node));
		}
		
		@Override
		public String getLabel()
		{
			return "URI reference";
		}
	}
	
	
	
}
